// 请补全JavaScript代码，要求实现对象参数的深拷贝并返回拷贝之后的新对象。
// 注意：
// 1. 需要考虑函数、正则、日期、ES6新对象
// 2. 需要考虑循环引用问题

/**
 * 
 * @param {拷贝对象} target 
 * @param {存储取值，解决循环引用问题} map 
 */
// const _completeDeepClone = (target, map = new Map()) => {
//     // 补全代码
//     if (target === null || typeof target !== 'object') {
//         return target;
//     }
//     if (map.has(target)) {
//         return map.get(target);
//     }
//     // 定义返回值
//     let ret;
//     let type = Object.prototype.toString.call(target);
//     // 日期或者正则，重新new一个
//     if (type == '[object Date]' || type == '[object RegExp]') {
//         ret = new target.constructor(target);
//     } else if (type === '[object Function]') {
//         ret = target.prototype.bind({})
//         // 函数好像可以直接使用
//         // ret = target;
//     } else if (Array.isArray(target)) {
//         ret = [...target];
//     } else {
//         // 对象的情况
//         ret = {};
//         // 解决循环引用问题
//         map.set(target, ret);
//         Object.keys(target).forEach(key => {
//             ret[key] = _completeDeepClone(target[key], map);
//         })
//     }
//     return ret;
// }

// let obj = {
//     name: 'zx',
//     hobby: ['play', 'eat', 'sing'],
//     parent: {
//         name: 'hjk',
//         hobby: ['smoke', 'drink']
//     },
//     do: function () {
//         console.log('i can do everything');
//     },
//     reg: /d/
// }
// obj.self = obj;
// let ret = _completeDeepClone(obj);

// obj.hobby.push('dance');
// obj.parent.hobby.push('sleep')
// console.log(ret);
// console.log(obj);




/**
 * 抄袭一手相对完整版的深克隆代码
 */

// 可以继续遍历的类型
const mapTag = '[object Map]';
const setTag = '[object Set]';
const arrayTag = '[object Array]';
const objectTag = '[object Object]';

// 不可以继续遍历的类型
const boolTag = '[object Boolean]';
const dateTag = '[object Date]';
const errorTag = '[object Error]';
const numberTag = '[object Number]';
const regexpTag = '[object RegExp]';
const stringTag = '[object String]';
const symbolTag = '[object Symbol]';
const funcTag = '[object Function]';

// 定义可以继续遍历的
const deep = [mapTag, setTag, arrayTag, objectTag];

/**
 * 深度克隆
 * @param {克隆对象} target 
 * @param {对象映射} map 
 * @returns 
 */
function deepClone(target, map = new WeakMap()) {
    // 基础类型直接返回
    if (!isObject(target)) {
        return target;
    }
    // 考虑循环引用情况
    if (map.has(target)) {
        return map.get(target);
    }

    let cloneTarget;
    const type = getType(target);
    if (deep.includes(type)) {
        cloneTarget = getInit(target);
    } else {
        return cloneOtherType(target, type);
    }
    map.set(target, cloneTarget);

    // 克隆set
    if (type === setTag) {
        target.forEach(value => {
            cloneTarget.add(deepClone(value, map));
        })
        return cloneTarget;
    }

    // 克隆map
    if (type === mapTag) {
        target.forEach((value, key) => {
            cloneTarget[key] = deepClone(value, map);
        })
        return cloneTarget;
    }

    // 克隆对象
    const keys = Array.isArray(target) ? undefined : Object.keys(target);
    // 将对象的key或者数组传入，循环执行回调。
    forEach(keys || target, (value, key) => {
        if (keys) {
            // 说明是对象的情况，value是具体的key，index是索引
            key = value;
        }
        cloneTarget[key] = deepClone(target[key], map);
    })
    return cloneTarget;
}

/**
 * 循环执行某个回调
 * 实际上循环的是对象的key或者数组的索引
 */
function forEach(array, callback) {
    let index = -1;
    while (index++ < array.length) {
        callback(array[index], index)
    }
    return array;
}

/**
 * 是否是Object
 * @param {判断对象} target 
 */
function isObject(target) {
    const type = typeof target;
    return target !== null && (type === 'object' || type === 'function')
}

/**
 * 获取目标对象的真实类型
 * @param {判断对象} object 
 */
function getType(object) {
    return Object.prototype.toString.call(object);
}

/**
 * 获取初始化对象
 * @param {目标对象} target 
 */
function getInit(target) {
    const Constructor = target.constructor;
    return new Constructor();
}

/**
 * 克隆不可迭代的类型
 * @param {目标对象} target 
 * @param {目标类型} type 
 * @returns 
 */
function cloneOtherType(target, type) {
    const Ctor = target.constructor;
    switch (type) {
        case boolTag:
        case numberTag:
        case stringTag:
        case errorTag:
        case dateTag:
            return new Ctor(target);
        case regexpTag:
            return cloneReg(target);
        case symbolTag:
            return cloneSymbol(target);
        case funcTag:
            return cloneFunciton(target);
        default:
            return null;
    }
}

/**
 * 克隆正则
 * @param {目标对象} target 
 */
function cloneReg(target) {
    const reFlags = /\w*$/;
    const result = new target.constructor(target.source, reFlags.exec(target));
    result.lastIndex = target.lastIndex;
    return result;
}

/**
 * 克隆symbol对象
 */
function cloneSymbol(target) {
    return Object(Symbol.prototype.valueOf.call(target));
}

/**
 * 克隆函数
 * 直接返回，不克隆
 */
function cloneFunciton(target) {
    return target;
}



const map = new Map();
map.set('key', 'value');
map.set('ConardLi', 'code秘密花园');

const set = new Set();
set.add('ConardLi');
set.add('code秘密花园');

const target = {
    field1: 1,
    field2: undefined,
    field3: {
        child: 'child'
    },
    field4: [2, 4, 8],
    empty: null,
    map,
    set,
    bool: new Boolean(true),
    num: new Number(2),
    str: new String(2),
    symbol: Object(Symbol(1)),
    date: new Date(),
    reg: /\d+/,
    error: new Error(),
    func1: () => {
        console.log('code秘密花园');
    },
    func2: function (a, b) {
        return a + b;
    }
};
console.log( deepClone(target) );